package com.primestone.cim.async;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.EJB;
import javax.ejb.MessageDriven;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.sql.DataSource;
import javax.xml.ws.Holder;

import org.jboss.ejb3.annotation.ResourceAdapter;

import ch.iec.tc57._2011.getmeterreadingsmessage.GetMeterReadingsRequestType;
import ch.iec.tc57._2011.schema.message.HeaderType;

import com.primestone.cim.meterreadings.Extractor;
import com.primestone.cim.meterreadings.beans.QueueMessage;
import com.primestone.cim.meterreadings.beans.ResMeterReadingsContainer;
import com.primestone.cim.service.GetMeterReadingsService;
import com.primestone.cim.service.IGetMeterReadingsService;
import com.primestone.cim.util.DateUtil;
import com.primestone.cim.util.SocketOrDevice;
import com.primestone.cim.util.StringUtil;
import com.primestone.cim.util.XMLUtil;
import com.primestone.cim.util.enums.SocDevEnum;
import com.primestone.cim.util.exceptions.ServiceException;

/**
 * Message-Driven Bean implementation class for: CIMMDB
 *
 */
@MessageDriven(	
		activationConfig = { 
				@ActivationConfigProperty(propertyName = "destinationType", propertyValue = StringUtil.QUEUE_DEST_TYPE),
				@ActivationConfigProperty( propertyName = "connectionFactoryJndiName", propertyValue = StringUtil.CONN_FACTORY_JNDI_NAME ),
				@ActivationConfigProperty(propertyName = "destination", propertyValue = StringUtil.DESTINATION_GET_MR_QUEUE),
				@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = StringUtil.ACK_MODE)
		}
		)
@ResourceAdapter(StringUtil.RES_ADAPTER)
@TransactionManagement(TransactionManagementType.BEAN)
public class CIMMDB implements MessageListener {
	
	private final static Logger LOGGER = Logger.getLogger(CIMMDB.class.getName());

	@Resource(name = StringUtil.RES_DS)
	private DataSource dataSource;
	
	@EJB( beanName = StringUtil.TMPTABLEEJB_BEAN_NAME )
	private TmpTableEJBLocal tmpTableEJB;

	private IGetMeterReadingsService getMeterReadingsService = GetMeterReadingsService.getInstance(this.dataSource);
	
	/**
	 * @see MessageListener#onMessage(Message)
	 */
	@Override
	public void onMessage(Message message) {
		try {
			QueueMessage queueMessage = (QueueMessage)XMLUtil.unmarchall(
					getByteArrayFromJmsMessage((BytesMessage)message), QueueMessage.class);

			String ticket = Extractor.extractTicket(queueMessage.getHeader());
			LOGGER.log(Level.INFO, "---- MDB comprobando ticket: {0}", ticket);

			processMessage( queueMessage );
			LOGGER.log(Level.INFO, "---- MDB Mensaje procesado, ticket: {0}", ticket);
		} catch( JMSException e ) {
			LOGGER.log(Level.WARNING, "---- MDB onMessage Exception: {0}", e.getMessage());
            //e.printStackTrace();
        } catch( Throwable e ) {
        	LOGGER.log(Level.WARNING, "---- MDB onMessage Throwable: {0}", e.getMessage());
        	//e.printStackTrace();
        }
	}

	/**
	 * 
	 * @param queueMessage
	 * @throws Exception
	 */
	private void processMessage( QueueMessage queueMessage ) throws Exception {
		try {
			Holder<HeaderType> header = queueMessage.getHeader();
			// obtener ticket del header.
			String ticket = Extractor.extractTicket(header);

			// Extract useful values from request and use it.
			GetMeterReadingsRequestType request = queueMessage.getRequest();

			// Extract the top parameter value.
			Integer top = Extractor.extractTop(request);

			// Extract the onlyNew parameter value.
			boolean onlyNew = Extractor.extractOnlyNewParameter(request);
			
			// Extract the varType parameter value.
			String varTypes = Extractor.extractVarType(request);
			
			// Extract the batch parameter.
			//boolean isBatch = Extractor.extractBatchParameter(request);
			
			Date startTime = null;
			if (DateUtil.safe(request.getStartTime())) {
				startTime = DateUtil.getJavaUtilDateFromXMLGregorianCalendar(request.getStartTime());	
			}

			// Iterar por los noins o idSockets que vengan en el respectivo option.
			String socketDeviceFlag = Extractor.extractSocketDeviceFlag(request);
			List<String> sockets = null;
			List<String> devices = null;
			if (socketDeviceFlag.equals(SocDevEnum.WORK_SOCKETS.getCode())) {
				String extractedSocket = Extractor.extractSocket(request);
				sockets = new SocketOrDevice(extractedSocket, SocDevEnum.WORK_SOCKETS).getSockets();
				Map<String, Date> allFromDates = null;
				ServiceException se;
				for (String idSocket : sockets) {
					se = null;
					try {
						allFromDates = this.getMeterReadingsService.getAllFromDates(idSocket, varTypes, startTime);
					} catch (ServiceException e) {
						// Ignore
						se = e;
					}
					
					savePagines(request, top, ticket, header.value, onlyNew, idSocket, SocDevEnum.WORK_SOCKETS, allFromDates, se);
				}
			} else { // Sino, se trata de WORK_DEVICES
				String extractedDevice = Extractor.extractDevice(request);
				devices = new SocketOrDevice(extractedDevice, SocDevEnum.WORK_DEVICES).getDevices();
				Map<String, Date> allFromDates = null;
				ServiceException se;
				for (String idDevice : devices) {
					se = null;
					try {
						allFromDates = this.getMeterReadingsService.getAllFromDates(idDevice, varTypes, startTime);
					} catch (ServiceException e) {
						se = e;
					}
					
					savePagines(request, top, ticket, header.value, onlyNew, idDevice, SocDevEnum.WORK_DEVICES, allFromDates, se);
				}
			}
		} catch (Exception e) {
			LOGGER.log(Level.WARNING, "---- MDB Exception: {0}", e.getMessage());
			//e.printStackTrace();
			throw e;
		}
	}
	
	/**
	 * 
	 * @param request
	 * @param top
	 * @param ticket
	 * @param value
	 * @param onlyNew
	 * @param idSocketOrDevice 
	 * @param socketOrDevice 
	 * @param allFromDates 
	 * @param e 
	 * @throws Exception
	 */
	private void savePagines(GetMeterReadingsRequestType request, Integer top, String ticket, HeaderType value, boolean onlyNew, 
			String idSocketOrDevice, SocDevEnum socketOrDevice, Map<String, Date> allFromDates, ServiceException se) throws Exception {
		int offset = 1;
		boolean end = false;
		ResMeterReadingsContainer container;
		Long idSavedMeterReadings = null;
		while (!end) {
			LOGGER.log(Level.FINE, "---- offset: {0}", offset);
			container = this.getMeterReadingsService.getReadings(request, top, ticket, value, offset, onlyNew, idSocketOrDevice, socketOrDevice, allFromDates, se);
			// Final de la iteracion cuando el resultado es vacio y no se debe a una excepcion (se asume que al no deberse a una excepcion se debe a que no hay mas datos en la base de datos).
			end = (container.isEmpty() && !container.isException()) || container.isException();
			idSavedMeterReadings = this.tmpTableEJB.saveResMeterReadings(container.getContent());
			LOGGER.log(Level.INFO, "---- idSavedMeterReadings: {0}", idSavedMeterReadings);
			offset += top;
		}
	}

	/**
	 * 
	 * @param bytesMessage
	 * @return
	 * @throws JMSException
	 */
	private static byte[] getByteArrayFromJmsMessage(BytesMessage bytesMessage) throws JMSException {
		byte data[] = new byte[(int) bytesMessage.getBodyLength()];
		bytesMessage.readBytes(data);
		return data;
	}

}
